package com.hc.java.md5;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.CountDownLatch;

public class MD5手机号破解 {

    public static void main(String[] args) throws NoSuchAlgorithmException {
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        String phone = "13012345678";
        byte[] md5Bytes = md5(md5, phone);
        String hash = toLowerCase(md5Bytes);
        System.out.println("hash: " + hash); // "2237a59d3b315490e5596dcea2337b05";

        CountDownLatch latch = new CountDownLatch(10);
        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(new Task(latch, hash, 10000000000L + (i * 1000000000L)));
        }

        long start = System.currentTimeMillis();
        System.out.println("Start time: " + start);
        for (Thread thread : threads) {
            thread.start();
        }

        try {
            latch.await();
//            for (Thread thread : threads) {
//                thread.join();
//            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();

        System.out.println("Time elapsed: " + (end - start) + " ms");
    }

    public static class Task implements Runnable {

        private final MessageDigest md5;
        private final CountDownLatch latch;
        private final String hash;

        private final long begin;

        public Task(CountDownLatch latch, String hash, long begin) {
            this.latch = latch;
            this.hash = hash;
            this.begin = begin;

            try {
                this.md5 = MessageDigest.getInstance("MD5");
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException(e.getMessage());
            }
        }

        @Override
        public void run() {
            Thread currentThread = Thread.currentThread();
            System.out.println("Thread " + currentThread.getId() + " working...");

            for (int i = 0; i < 1000000000; i++) {
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("Thread " + currentThread.getId() + " interrupted!");
                    break;
                }
                String phone = String.valueOf(begin + i);
                byte[] md5 = md5(this.md5, phone);
                if (toLowerCase(md5).equals(hash)) {
                    System.out.println("phone: " + phone + "  ms:" + System.currentTimeMillis());
                }
            }

            System.out.println("Thread " + currentThread.getId() + " finished.  " + System.currentTimeMillis());
            latch.countDown();
        }
    }

    public static byte[] md5(MessageDigest md5, String message) {
        if (null == message || message.isEmpty()) {
            throw new IllegalArgumentException("message is empty!");
        }

        md5.update(message.getBytes());
        byte[] hash = md5.digest();

        return hash;
    }

    public static String toLowerCase(byte[] hash) {
        if (null == hash) {
            return null;
        }

        if (0 == hash.length) {
            return "";
        }

        //[0-9]       == [0-9]              == [0b0000_0000-0b0000_1001]
        //['0'-'9']   == [48-57]            == [0b0011_0000-0b0011_1001]
        //[0-16]      == [0-16]             == [0b0000_0000-0b0000_1111]
        //['0'-'F']   == [48-57] + [65-79]  == [0b0011_0000-0b0011_1001] + [0b0100_0001-0b0100_1111]
        //['0'-'f']   == [48-57] + [97-102] == [0b0011_0000-0b0011_1001] + [0b0110_0001-0b0110_1111]
        //['A'-'Z']   == [65-90]            == [0b0100_0001-0b0101_1010]
        //['a'-'z']   == [97-122]           == [0b0110_0001-0b0111_1010]
        char[] hex = new char[hash.length << 1];
        for (int i = 0; i < hash.length; i++) {
            int idx = i << 1;

            int high = (hash[i] & 0b11110000) >> 4;
            if (high < 10) {
                hex[idx] = (char) (high | 48); //变成字符0-9
            } else {
                hex[idx] = (char) (high + 87); //变成字符a-f
            }

            int low = hash[i] & 0b00001111;
            if (low < 10) {
                hex[idx + 1] = (char) (low | 48); //变成字符0-9
            } else {
                hex[idx + 1] = (char) (low + 87); //变成字符a-f
            }
        }

        return new String(hex);
    }
}